﻿using System;
using System.Linq;
using LinqExpressions = System.Linq.Expressions;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Silverlight.Testing;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Reflection;
using System.Windows.Threading;

using Caliburn.Micro.BikeWizard.Tests.BaseClasses;
using Caliburn.Micro.BikeWizard.Tests.Utils;
using Caliburn.Micro.BikeWizard.Views;
using Caliburn.Micro.BikeWizard.Utils;

namespace Caliburn.Micro.Wizard.Tests
{
    /// <summary>
    /// Testclass that tests the bike wizard viewmodel and the different viewmodels it creates for the different wizard screens.
    /// Because the viewmodel has no knowledge of the view, the viewmodel can be tested in isolation.
    /// That's a real strength of the MVVM pattern.
    /// 
    /// The tests rely on the testdata being set up correctly. This is done in by the TestDataHelper, which is instantiated in the bootstrapper.
    /// If you change the testdata setup you will have to change the unittests.
    /// </summary>
    [TestClass]
    public class ViewModelTests : TestBase
    {
        #region Initialization
        /// <summary>
        /// Important: Any exceptions that occur in the Init() method are swallowed
        /// </summary>
        [TestInitialize]
        [Asynchronous]
        public void InitTest()
        {
            new CoroutineExecutor(
                bikeOptionService.ClearData(),
                 new TestDataHelper().SetupTestData(bikeOptionService),
                SetTestComplete()).Execute(null);
        }
        #endregion

        #region Tests
        [TestMethod]
        [Asynchronous]
        public void BikeWizardViewModel_Test()
        {
            new CoroutineExecutor(BikeWizardViewModel_TestImpl()).Execute(null);
        }
        #endregion

        #region Test Implementations
        /// <summary>
        /// Walk through the wizard and check if the different viewmodels have the correct properties
        /// Whether those properties are correctlty bound to view controls is of no concern here. That's what the BindingTests are for.
        /// </summary>
        /// <returns></returns>
        private IEnumerable<IResult> BikeWizardViewModel_TestImpl()
        {
            var bikeWizardFactory = IoC.Get<BikeWizardViewModelFactory>();
            var bikeWizardViewModel = bikeWizardFactory.CreateInstance();

            yield return new ActionResult(
                (actionResult) =>
                {
                    Assert.IsTrue(bikeWizardViewModel.ActiveItem is WelcomeViewModel);
                    Assert.IsTrue(bikeWizardViewModel.CanMoveNext);
                    Assert.IsFalse(bikeWizardViewModel.CanMovePrevious);

                    actionResult.SetComplete(this);
                });

            yield return 
                new MultipleEventWaiter(
                    () => bikeWizardViewModel.MoveNext(),
                    new SingleEventWaiter(bikeWizardViewModel, "ActivationProcessed"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    var activeViewModel = bikeWizardViewModel.ActiveItem as BikeTypeFrameSizeViewModel;

                    Assert.IsNotNull(activeViewModel);
                    Assert.IsFalse(bikeWizardViewModel.CanMoveNext);
                    Assert.IsTrue(bikeWizardViewModel.CanMovePrevious);

                    Assert.AreEqual(5, activeViewModel.AvailableBikeTypes.Count);
                    Assert.AreEqual(3, activeViewModel.AvailableFrameSizes.Count);

                    Assert.IsNotNull(activeViewModel.SelectedBikeType);
                    Assert.IsNull(activeViewModel.SelectedFrameSize);
                    Assert.AreEqual("Hardtail MTB", activeViewModel.SelectedBikeType.DisplayName);

                    activeViewModel.AvailableFrameSizes[0].IsSelected = true;
                    Assert.IsNotNull(activeViewModel.SelectedFrameSize);
                    Assert.AreEqual(activeViewModel.AvailableFrameSizes[0].GetValue(), activeViewModel.SelectedFrameSize);

                    Assert.IsTrue(bikeWizardViewModel.CanMoveNext);
                    Assert.IsTrue(bikeWizardViewModel.CanMovePrevious);

                    actionResult.SetComplete(null);
                });

            yield return
                new MultipleEventWaiter(
                    () => bikeWizardViewModel.MoveNext(),
                    new SingleEventWaiter(bikeWizardViewModel, "ActivationProcessed"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    var activeViewModel = bikeWizardViewModel.ActiveItem as BikeExtrasViewModel;

                    Assert.IsNotNull(activeViewModel);
                    Assert.IsTrue(bikeWizardViewModel.CanMoveNext);
                    Assert.IsTrue(bikeWizardViewModel.CanMovePrevious);

                    Assert.AreEqual(3, activeViewModel.AvailablePartsGroups.Count);
                    Assert.AreEqual(6, activeViewModel.AvailableExtras.Count);

                    Assert.IsNotNull(activeViewModel.SelectedPartsGroup);
                    Assert.AreEqual("Shimano XT", activeViewModel.SelectedPartsGroup.DisplayName);

                    Assert.IsNotNull(activeViewModel.SelectedExtras);
                    Assert.AreEqual(2, activeViewModel.SelectedExtras.Count);
                    Assert.IsNotNull(activeViewModel.SelectedExtras.SingleOrDefault((extra) => extra.DisplayName.Equals("Bike Trousers")));
                    Assert.IsNotNull(activeViewModel.SelectedExtras.SingleOrDefault((extra) => extra.DisplayName.Equals("Rain Jacket")));

                    actionResult.SetComplete(null);
                });

            yield return
                new MultipleEventWaiter(
                    () => bikeWizardViewModel.MoveNext(),
                    new SingleEventWaiter(bikeWizardViewModel, "ActivationProcessed"));

            yield return new ActionResult(
                (actionResult) =>
                {
                    var activeViewModel = bikeWizardViewModel.ActiveItem as BikeSummaryViewModel;

                    Assert.IsNotNull(activeViewModel);
                    Assert.IsTrue(bikeWizardViewModel.CanMoveNext);
                    Assert.IsTrue(bikeWizardViewModel.CanMovePrevious);
                    Assert.AreEqual("Finish", bikeWizardViewModel.MoveNextCaption);

                    actionResult.SetComplete(null);
                });

            yield return SetTestComplete();
        }
        #endregion
    }
}
